package name.zeno.qrcode.library.hui

import android.graphics.*
import com.google.zxing.qrcode.encoder.ByteMatrix
import name.zeno.android.util.ZBitmap


/**
 * @author 陈治谋 (513500085@qq.com)
 * @since 2017/11/30
 */
object QRCodeDrawer {
  private val paint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) }
  private val rect by lazy { RectF() }

  private fun RectF.set(l: Int, t: Int, r: Int, b: Int) = set(l.toFloat(), t.toFloat(), r.toFloat(), b.toFloat())

  /** * 米黄色 */
  var OFF_WHITE = Color.parseColor("#08112A")


  /**
   * 根据路径获取图片
   *
   * @param path 本地路径 or 网络地址
   * @return 图片
   * @throws IOException
   */
  fun getImageByPath(path: String): Bitmap? = ZBitmap.bitmap(path)


//  /**
//   * 图片旋转
//   * @param source
//   * @param rotate
//   * @return
//   */
//  fun rotateImg(source: Bitmap, rotate: Int): Bitmap {
//    if (rotate % 360 == 0) {
//      return source
//    }
//
//    val tx = AffineTransform()
//    tx.rotate(Math.toRadians(rotate.toDouble()), (source.getWidth() shr 1).toDouble(), (source.getHeight() shr 1).toDouble())
//
//    val op = AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR)
//    return op.filter(source, null)
//  }


  /**
   * 绘制logo图片
   * 1. 圆角Clip
   * 2. 绘制背景
   * 3. 绘制Logo
   * 4. 绘制边框
   *
   * @param qrcode
   * @param codeOptions
   * @return
   */
  fun drawLogo(qrcode: Bitmap, codeOptions: QrCodeOptions): Bitmap {
    logo(codeOptions)?.apply {
      val codeW = qrcode.width
      val codeH = qrcode.height

      // logo的宽高
      // 不放大，只缩小，如果给的logo 过小, 按给的 logo 绘制
      val logoRate = codeOptions.logoRate
      val w = Math.min(width.toFloat(), codeW * logoRate)
      val h = Math.min(height.toFloat(), codeH * logoRate)

      val x = (codeW - w) / 2
      val y = (codeH - h) / 2

      // 插入LOGO
      val canvas = Canvas(qrcode)
      rect.set(x, y, x + w, y + h)
      canvas.drawBitmap(this, null, rect, null)
    }
    return qrcode
  }

  /**
   * 绘制logo图片
   * 1. 圆角Clip
   * 2. 绘制背景
   * 3. 绘制Logo
   * 4. 绘制边框
   *
   * @param qrcode
   * @param codeOptions
   * @return
   */
  fun logo(options: QrCodeOptions): Bitmap? {
    val path = Path()
    val logo = options.logo ?: return null
    var w = logo.width
    var h = logo.height

    if (w > 100) {
      val r = 100F / w
      w = (w * r).toInt()
      h = (h * r).toInt()
    }

    var borderW = 0
    if (options.logoBorderColor != Color.TRANSPARENT) {
      borderW = Math.min(4, w / 15)
    }

    val canvasW = w + borderW
    val canvasH = h + borderW
    val result = Bitmap.createBitmap(canvasW, canvasH, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(result)

    // 圆角半径
    var round = options.logoRound.toFloat()
    round = Math.max(round, 0F)
    round = Math.min(round, w / 2F)

    // 1. 圆角裁切
    if (round > 0) {
      rect.set(0, 0, canvasW, canvasH)
      path.reset()
      path.addRoundRect(rect, round, round, Path.Direction.CW)
      canvas.clipPath(path)
    }

    // 2. 背景色
    if (options.logoBgColor != Color.TRANSPARENT) {
      canvas.drawColor(options.logoBgColor)
    }

    // 3. 边框
    if (options.logoBorderColor != Color.TRANSPARENT) {
      paint.color = options.logoBorderColor
      paint.style = Paint.Style.STROKE
      val borderWidth = 4F
      paint.strokeWidth = borderWidth
      val offset = borderW / 2F
      rect.set(offset, offset, canvasW - offset, canvasH - offset)
      canvas.drawRoundRect(rect, round, round, paint)
    }

    // logo
    rect.set(borderW, borderW, canvasW - borderW, canvasH - borderW)
    path.reset()
    path.addRoundRect(rect, round, round, Path.Direction.CW)
    canvas.clipPath(path)
    canvas.drawBitmap(logo, null, rect, null)

    return result
  }

  /**
   * 生成边框
   *
   * @param logo        原图
   * @param cornerRadius 角度（根据实测效果，一般建议为图片宽度的1/4）, 0表示直角
   * @param color        边框颜色
   * @return
   */
  private fun makeRoundBorder(logo: Bitmap, cornerRadius: Int, color: Int): Bitmap {
    val size = logo.width / 15
    val w = logo.width + size
    val h = logo.height + size
    val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)

    val canvas = Canvas(bitmap)
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = size.toFloat()
    paint.color = color
    rect.set(0, 0, w, h)
    canvas.drawRoundRect(rect, cornerRadius.toFloat(), cornerRadius.toFloat(), paint)
    canvas.drawBitmap(logo, size.toFloat(), size.toFloat(), null)
    return bitmap
  }


  /**
   * 生成圆角图片
   *
   * @param image        原始图片
   * @param cornerRadius 圆角的弧度（根据实测效果，一般建议为图片宽度的1/4）, 0表示直角
   * @return 返回圆角图
   */
  private fun makeRoundedCorner(image: Bitmap, cornerRadius: Int): Bitmap {
    val w = image.width
    val h = image.height
    val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)

    val canvas = Canvas(bitmap)
    paint.color = Color.WHITE
    rect.set(0, 0, w, h)
    canvas.drawRoundRect(rect, cornerRadius.toFloat(), cornerRadius.toFloat(), paint)
    canvas.drawBitmap(image, 0F, 0F, null)

    return bitmap
  }


  /**
   * 绘制背景图
   *
   * @param code       二维码图
   * @param bgImgOptions 背景图信息
   * @return
   */
  fun drawBackground(code: Bitmap, bgImgOptions: BgImgOptions): Bitmap {
    val sW = code.width
    val sH = code.height

    // 背景的图宽高不应该小于二维码
    val bgW = Math.max(bgImgOptions.bgW, sH)
    val bgH = Math.max(bgImgOptions.bgH, sW)


    // 背景图缩放
    var bg = bgImgOptions.bgImg
    if (bg.width != bgW || bg.height != bgH) {
      val temp = Bitmap.createBitmap(bgW, bgH, Bitmap.Config.ARGB_8888)
      Canvas(temp).drawBitmap(ZBitmap.zoom(bg, bgW.toDouble(), bgH.toDouble()), 0F, 0F, null)
      bg = temp
    }

    val canvas = Canvas(bg)
    if (bgImgOptions.bgImgStyle == BgImgOptions.FILL) {
      // 选择一块区域进行填充
//      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f))
//      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
      canvas.drawBitmap(code, bgImgOptions.startX.toFloat(), bgImgOptions.startY.toFloat(), null)
    } else {
      // 覆盖方式
      val x = bgW - sW
      val y = bgH - sH
//      val x = bgW - sW shr 1
//      val y = bgH - sH shr 1

//      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, bgImgOptions.getOpacity())) // 透明度， 避免看不到背景
//      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
//      g2d.drawImage(source, x, y, sW, sH, null)
      paint.alpha = (bgImgOptions.alpha * 255).toInt()
      canvas.drawBitmap(code, x.toFloat(), y.toFloat(), paint)
//      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f))
    }
//    g2d.dispose()
//    bg.flush()
    return bg
  }


  fun drawQrInfo(qrCodeConfig: QrCodeOptions, bitMatrix: BitMatrixEx): Bitmap {
    val qrCodeWidth = bitMatrix.width
    val qrCodeHeight = bitMatrix.height
    val infoSize = bitMatrix.multiple
    val qrCode = Bitmap.createBitmap(qrCodeWidth, qrCodeHeight, Bitmap.Config.ARGB_8888)

    var bgColor = qrCodeConfig.drawOptions.bgColor          // 绘制的背景色
    val preColor: Int = qrCodeConfig.drawOptions.preColor   // 绘制前置色

    if (qrCodeConfig.bgImgOptions != null && qrCodeConfig.bgImgOptions!!.bgImgStyle == BgImgOptions.PENETRATE) {
      // 透传，用背景图颜色进行绘制时
      bgColor = Color.TRANSPARENT
    }

    val detectOutColor = qrCodeConfig.detectOutColor  // 探测图形外圈的颜色
    val detectInnerColor = qrCodeConfig.detectInColor // 探测图形内圈的颜色


    val leftPadding = bitMatrix.leftPadding
    val topPadding = bitMatrix.topPadding

    val canvas = Canvas(qrCode)

    // 直接背景铺满整个图
    paint.style = Paint.Style.FILL
    paint.color = bgColor
    rect.set(0, 0, qrCodeWidth, qrCodeHeight)
    canvas.drawRect(rect, paint)

    // 探测图形的大小
    val detectCornerSize = if (bitMatrix.byteMatrix.get(0, 5).toInt() == 1) 7 else 5

    val byteW = bitMatrix.byteMatrix.width
    val byteH = bitMatrix.byteMatrix.height

    var row2 = false
    var col2 = false
    val drawStyle = qrCodeConfig.drawOptions.drawStyle
    for (x in 0 until byteW) {
      for (y in 0 until byteH) {
        // 白色 不绘制
        if (bitMatrix.byteMatrix.get(x, y).toInt() == 0) continue

        /**
         * xxx    xxx
         * xxx    xxx
         *
         * xxx
         * xxx
         */
        // 设置三个位置探测图形
        if ((x < detectCornerSize && y < detectCornerSize) // 左上角
            || (x < detectCornerSize && y >= byteH - detectCornerSize) // 左下脚
            || (x >= byteW - detectCornerSize && y < detectCornerSize)) { // 右上角
          for (offsetX in 0 until detectCornerSize) {
            for (offsetY in 0 until detectCornerSize) {
              bitMatrix.byteMatrix[x + offsetX, y + offsetY] = 0
            }
          }

          val detectImg = qrCodeConfig.detectImg
          if (detectImg != null) {
            val l = leftPadding + x * infoSize
            val t = topPadding + y * infoSize
            rect.set(
                l, t,
                l + detectCornerSize * infoSize,
                t + detectCornerSize * infoSize
            )
            canvas.drawBitmap(detectImg, null, rect, null)
            continue
          }

          val detectR = qrCodeConfig.detectRound.toFloat()
          paint.color = detectOutColor
          paint.style = Paint.Style.STROKE
          paint.strokeWidth = infoSize.toFloat()
          rect.set(
              leftPadding + (x + 0.5F) * infoSize,
              topPadding + (y + 0.5F) * infoSize,
              leftPadding + (x + detectCornerSize - 0.5F) * infoSize,
              topPadding + (y + detectCornerSize - 0.5F) * infoSize
          )
          canvas.drawRoundRect(rect, detectR, detectR, paint)


          paint.color = detectInnerColor
          paint.style = Paint.Style.FILL
          paint.strokeWidth = 0F
          rect.set(
              leftPadding + (x + 2) * infoSize,
              topPadding + (y + 2) * infoSize,
              leftPadding + (x + detectCornerSize - 2) * infoSize,
              topPadding + (y + detectCornerSize - 2) * infoSize
          )
          canvas.drawRoundRect(rect, detectR, detectR, paint)
        } else { // 着色二维码主题
          paint.color = preColor
          paint.strokeWidth = 0F
          paint.style = Paint.Style.FILL
//
          if (!qrCodeConfig.drawOptions.enableScale) {
            drawStyle.draw(canvas, paint,
                leftPadding + x * infoSize,
                topPadding + y * infoSize,
                infoSize,
                infoSize,
                qrCodeConfig.drawOptions.img)
            continue
          }


          val mat = bitMatrix.byteMatrix

          // 支持拓展时
          row2 = rightTrue(mat, x, y)
          col2 = belowTrue(mat, x, y)

          if (row2 && col2 && diagonalTrue(mat, x, y) && qrCodeConfig.drawOptions.enableScale(ExpandType.SIZE4)) {
            // 四个相等
            mat[x + 1, y] = 0
            mat[x + 1, y + 1] = 0
            mat[x, y + 1] = 0

            drawStyle.draw(canvas, paint,
                leftPadding + x * infoSize, topPadding + y * infoSize,
                infoSize * 2, infoSize * 2,
                qrCodeConfig.drawOptions.size4Img
            )
          } else if (row2 && qrCodeConfig.drawOptions.enableScale(ExpandType.ROW2)) { // 横向相同
            bitMatrix.byteMatrix.set(x + 1, y, 0)
            drawStyle.draw(canvas, paint,
                leftPadding + x * infoSize,
                topPadding + y * infoSize,
                infoSize shl 1,
                infoSize,
                qrCodeConfig.drawOptions.row2Img)
          } else if (col2 && qrCodeConfig.drawOptions.enableScale(ExpandType.COL2)) { // 列的两个
            bitMatrix.byteMatrix.set(x, y + 1, 0)
            drawStyle.draw(canvas,
                paint,
                leftPadding + x * infoSize,
                topPadding + y * infoSize,
                infoSize,
                infoSize shl 1,
                qrCodeConfig.drawOptions.col2img)
          } else {
            drawStyle.draw(canvas, paint,
                leftPadding + x * infoSize,
                topPadding + y * infoSize,
                infoSize,
                infoSize,
                qrCodeConfig.drawOptions.img)
          }
        }
      } // end for y
    } // end for x
    return qrCode
  }


  private fun rightTrue(byteMatrix: ByteMatrix, x: Int, y: Int): Boolean {
    return x + 1 < byteMatrix.width && byteMatrix.get(x + 1, y).toInt() == 1
  }

  private fun belowTrue(byteMatrix: ByteMatrix, x: Int, y: Int): Boolean {
    return y + 1 < byteMatrix.height && byteMatrix.get(x, y + 1).toInt() == 1
  }

  // 对角是否相等
  private fun diagonalTrue(byteMatrix: ByteMatrix, x: Int, y: Int): Boolean {
    return byteMatrix.get(x + 1, y + 1).toInt() == 1
  }
}